﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PServiceBus.Core.Logger;
using PServiceBus.Gateway.Interfaces.Commands;
using PServiceBus.Core.Runtime;
using PServiceBus.Core.Interface;
using System.Collections.Concurrent;
using PServiceBus.Core.Manager;
using PServiceBus.Core.Security;

namespace PServiceBus.Gateway.Interfaces {

    public abstract class MessageServiceBase {
        private static IAuthenticationProvider _authenticationProvider;
        private static ConcurrentDictionary<string, IMessageBus> _messageBuses;

        static MessageServiceBase() {
            _authenticationProvider = SecurityManager.AuthenticationProvider;
            _messageBuses = new ConcurrentDictionary<string, IMessageBus>();
        }

        public static IMessageBus GetMessageBus(ESBUserContext context, bool reThrowException = false, Dictionary<string, string> headers = null) {
            var messageBus = default(IMessageBus);
            var username = context.UserName;
            var password = context.Password;
            var key = String.Concat(username, password);
            if (!_messageBuses.TryGetValue(key, out messageBus)) {
                messageBus = new MessageBus() { ReThrowException = reThrowException, Headers = headers };
                try {
                    messageBus.Identity =
                        new ESBIdentity(username, password,
                            _authenticationProvider.Authenticate(username, password));
                    _messageBuses[key] = messageBus;
                } catch {
                }
            }
            return messageBus;
        }
    }

    public abstract class MessageServiceBase<TCommand> : MessageServiceBase, IDisposable  where TCommand : MessageBase {
        private bool _disposed;
        private static Dictionary<string, Action<TCommand>> _commands;

        private IGatewayServer _server;
       
        protected MessageServiceBase(string serverAddress) {
            _commands = new Dictionary<string, Action<TCommand>>();
            var gatewayServer = ServiceLocator<IGatewayServer>.Get();
            if (gatewayServer == null)
                throw new InvalidOperationException("Could not locate IGatewayServer implementation using ServiceLocator, please define it");
            _server = gatewayServer.Init(serverAddress);
            Setup();
        }

        private void Setup() {
            RegisterRequestResponse();
            RegisterCommands();
            _server.Register<TCommand>(OnExecuteCommand);
            _server.ErrorReceived = ex => HandleError(ex);
            _server.Start();
        }

        private void HandleError(Exception ex) {
            var error = ex.InnerException != null ? ex.InnerException : ex;
            ESBLogger.Log(error);
        }

        private static void OnExecuteCommand(TCommand command) {
            _commands[command.CommandName](command);
        }

        protected IGatewayServer Server { get { return _server; } }

        protected abstract void RegisterRequestResponse();

        protected abstract void RegisterCommands();

        protected void RegisterCommand(Action<TCommand> action) {
            _commands[action.Method.Name] = action;
        }

        #region IDisposable Members

        public virtual void Dispose() {
            if (_disposed) return;
            if (_server != null) _server.Dispose();
            _server = null;
            _disposed = true;
        }

        #endregion
    }
}
